#pragma once

#include "data.hpp"
#include "httplib.h"
#include <time.h>

extern cloud::dataManager *data;

namespace cloud
{
    class server
    {
    private:
        size_t _server_port;
        std::string _server_ip;
        std::string _prefix_download;
        httplib::Server _server;

    private:
        // 上传请求
        static void upload(const httplib::Request &req, httplib::Response &res)
        {
            if (req.has_file("file") == false)
            {
                res.status = 400;
                return;
            }

            // 处理请求
            const auto &file = req.get_file_value("file");
            std::string backdir = config::getInstance()->getUrlBackup();
            std::string url = backdir + FileTool(file.filename).getFileName();

            // 创建文件，添加管理
            FileTool ft(url);
            ft.setContent(file.content);
            backupData bd;
            bd.setBackupdata(url);
            data->insert(bd);

            // res.status = 200;
        }

        static std::string timeToStr(const time_t& tm){
            struct tm *t = localtime(&tm);
            std::string tmp = "最后一次修改时间：";
            tmp += std::to_string(t->tm_year + 1900); 
            tmp += "年"; 
            tmp += std::to_string(t->tm_mon + 1); 
            tmp += "月";
            tmp += std::to_string(t->tm_mday);
            tmp += "日 ";
            tmp += std::to_string(t->tm_hour);
            tmp += ":";
            tmp += std::to_string(t->tm_min);
            
            return tmp;
        }

        // 展示请求
        static void show(const httplib::Request &req, httplib::Response &res)
        {
            std::vector<backupData> arry;
            data->getAll(&arry);

            // 根据所有备份信息，组织html文件数据
            std::stringstream ss;
            ss << R"(<html><head><meta charset="UTF-8"><title>Download</title></head>)";
            ss << "<body><h1>Download</h1><table>";
            for (auto &a : arry){
                ss << "<tr>";
                std::string filename = FileTool(a.url_real).getFileName();
                ss << "<td><a href='" << a.url << "'>" << filename << "</a></td>";
                ss << "<td align='right'>" << timeToStr(a.mtime) << "</td>";
                ss << "<td align='right'>" << a.size / 1024 << "KB</td>";
                ss << "</tr>";
            }
            ss << "</table></body></html>";
            res.body = ss.str();
            res.set_header("Content-Type", "text/html");
            // res.status = 200;
        }

        static std::string getETag(const backupData& bd){
            std::string tmp = FileTool(bd.url_real).getFileName();
            tmp += "-";
            tmp += std::to_string(bd.mtime);
            tmp += "-";
            tmp += std::to_string(bd.size);
            return tmp;
        }

        // 下载请求
        static void download(const httplib::Request &req, httplib::Response &res)
        {
            backupData bd;
            data->getOneUrl(req.path, &bd);

            if(bd.is_pack){
                FileTool ft(bd.url_pack);
                ft.unzip(bd.url_real);
                bd.is_pack = true;
                ft.remove();
                
                data->updata(bd);
            }

            FileTool ft(bd.url_real);
			ft.getAllContent(&res.body);
			// 设置响应头部字段： ETag， Accept-Ranges: bytes
			res.set_header("Accept-Ranges", "bytes");
			res.set_header("ETag", getETag(bd));
			res.set_header("Content-Type", "application/octet-stream");

                // 有If-Range字段并且与文件最新ETag字段一致，则断点续传
            if(req.has_header("If-Range") && req.get_header_value("If-Range") == getETag(bd)){
                res.status = 206;
            }
            else{
                res.status = 200;
            }

        }

    public:
        server()
        {
            _server_port = config::getInstance()->getServerPort();
            _server_ip = config::getInstance()->getServerIP();
            _prefix_download = config::getInstance()->getUrlDownload();
        }

        void start()
        {
            _server.Post("/upload", upload);
            _server.Get("/show", show);
            _server.Get("/", show);
            std::string tmp = _prefix_download + "(.*)";
            _server.Get(tmp, download);
            if (_server.listen(_server_ip.c_str(), _server_port))
            {
                std::cout << "server listen error" << std::endl;
                return;
            }
        }
    };

}